package com.yfp.machine.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.yfp.common.core.constant.DbConstants;
import com.yfp.common.core.constant.EmqxConstants;
import com.yfp.common.core.domain.R;
import com.yfp.common.core.exception.ServiceException;
import com.yfp.common.core.utils.bean.BeanUtils;
import com.yfp.common.security.utils.SecurityUtils;
import com.yfp.emqx.MqttSendClient;
import com.yfp.machine.domain.*;
import com.yfp.machine.domain.*;
import com.yfp.machine.domain.dto.AdDto;
import com.yfp.machine.domain.dto.MachineAdDto;
import com.yfp.machine.domain.dto.MachineAdObj;
import com.yfp.machine.domain.dto.MachineDto;
import com.yfp.machine.domain.vo.MachineAdAddVo;
import com.yfp.machine.domain.vo.MachineAdVo;
import com.yfp.machine.enums.PalyAdPositionEnum;
import com.yfp.machine.enums.PalyStatusEnum;
import com.yfp.machine.mapper.MachineAdMapper;
import com.yfp.machine.service.*;
import com.yfp.machine.service.*;
import jakarta.annotation.Resource;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.stereotype.Service;

import java.util.*;


/**
 * (MachineAd)表服务实现类
 *
 * 
 * @since 2023-12-16 21:24:25
 */
@Service("machineAdService")
@Slf4j
public class MachineAdServiceImpl implements MachineAdService {
    @Resource
    private MachineAdMapper machineAdMapper;

    @Resource
    private MachineService machineService;

    @Resource
    private MqttSendClient mqttSendClient;

    @Resource
    private AdService adService;

    @Resource
    private AdResourceService adResourceService;
    @Resource
    private VideoResourceService videoResourceService;
    @Resource
    private ImgResourceService imgResourceService;

    @Resource
    private AdPushRecordService adPushRecordService;

    @Override
    public boolean pushMachineAd(MachineAd machineAd, String machineId) {

        MachineDto machineDto = machineService.queryMachineInfoById(machineId);
        if (ObjectUtil.isNull(machineDto)) return false;

        AdDto adDto = adService.queryById(machineAd.getAdId());
        if (ObjectUtil.isNull(adDto)) return false;

        AdResource adResource = adResourceService.queryById(machineAd.getAdId());
        if (ObjectUtil.isNull(adResource)) return false;

        String recordNo = UUID.randomUUID().toString();

        boolean result = pushAdMsg(machineId, machineAd, adResource, adDto, recordNo);

        if (result) {
            savePushAdLog(adDto, recordNo, machineAd);
        }

        machineAd.setPalyStatus(PalyStatusEnum.PLAYING.getCode());
        this.machineAdMapper.update(machineAd);
        return result;
    }


    private void savePushAdLog(AdDto adDto, String recordNo, MachineAd machineAd) {
        AdPushRecord adPushRecord = new AdPushRecord();
        adPushRecord.setAdId(adDto.getAdId());
        adPushRecord.setAdType(adDto.getType());
        adPushRecord.setMachineId(machineAd.getMachineId());
        adPushRecord.setRecordNo(recordNo);
        adPushRecord.setPalyBeginTime(machineAd.getPalyBeginTime());
        adPushRecord.setPlayEndTime(machineAd.getPlayEndTime());
        adPushRecord.setPalyAdPosition(machineAd.getPalyAdPosition());
        adPushRecordService.insert(adPushRecord);
    }

    private boolean pushAdMsg(String machineId, MachineAd machineAd, AdResource adResource, AdDto adDto, String recordNo) {
        JSONArray adList = new JSONArray();

        Integer adType = machineAd.getAdType();

        MachineAdObj machineAdObj = setAdInfo(adType, adResource);

        machineAdObj.setAdId(adDto.getAdId());
        machineAdObj.setAdName(adDto.getName());
        machineAdObj.setAdPosition(machineAd.getPalyAdPosition());

        adList.add(machineAdObj);
        JSONObject pushObj = new JSONObject();

        pushObj.set(EmqxConstants.MachineAdType.TOPIC_PUSH_AD_LIST, adList);
        pushObj.set(EmqxConstants.TOPIC_PUSH_RECORD_NO, recordNo);
        pushObj.set(EmqxConstants.MachineAdType.TOPIC_PUSH_START_TIME, machineAd.getPalyBeginTime());
        pushObj.set(EmqxConstants.MachineAdType.TOPIC_PUSH_END_TIME, machineAd.getPlayEndTime());
        pushObj.set(EmqxConstants.TOPIC_PUSH_MSG_TYPE, EmqxConstants.TOPIC_PUSH_ADS);
        pushObj.set(EmqxConstants.TOPIC_PUSH_TIME, DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));

        try {
            mqttSendClient.publish(false, machineId, pushObj.toString());
        } catch (MqttException e) {
            return false;
        }
        return true;
    }

    private MachineAdObj setAdInfo(Integer adType, AdResource adResource) {
        MachineAdObj machineAdObj = new MachineAdObj();
        machineAdObj.setAdType(adType.toString());

        if (Objects.equals(DbConstants.machineCorrelation.AD_TYPE_IMG, adType)) {
            ImgResource imgResource = imgResourceService.queryById(adResource.getResourceId());
            machineAdObj.setAdUrl(imgResource.getUrl());
        } else if (Objects.equals(DbConstants.machineCorrelation.AD_TYPE_VIDEO, adType)) {
            VideoResource videoResource = videoResourceService.queryById(adResource.getResourceId());
            machineAdObj.setAdUrl(videoResource.getUrl());
        } else {
            throw new ServiceException("广告类型不支持");
        }
        return machineAdObj;
    }


    @Override
    public List<MachineAd> queryMachineAdvertisingListByMachineId(String machineId) {

        List<MachineAd> machineAds = this.machineAdMapper.queryAllByMachineAd(machineId);

        if (CollUtil.isEmpty(machineAds)) return Collections.emptyList();

        for (MachineAd machineAd : machineAds) {

            setAdInfo(machineAd);

        }
        return machineAds;
    }

    private void setAdInfo(MachineAd machineAd) {
        if (ObjectUtil.isNull(machineAd.getAdType())) return;

        AdDto adDto = this.adService.queryById(machineAd.getAdId());
        if (ObjectUtil.isNotNull(adDto)) machineAd.setAdName(adDto.getName());

        AdResource adResource = adResourceService.queryById(machineAd.getAdId());
        if (Objects.equals(DbConstants.machineCorrelation.AD_TYPE_IMG, adResource.getResourceType())) {

            ImgResource imgResource = imgResourceService.queryById(adResource.getResourceId());
            if (ObjectUtil.isNotNull(imgResource)) machineAd.setAdUrl(imgResource.getUrl());
        }

        if (Objects.equals(DbConstants.machineCorrelation.AD_TYPE_VIDEO, adResource.getResourceType())) {
            VideoResource videoResource = videoResourceService.queryById(adResource.getResourceId());
            if (ObjectUtil.isNotNull(videoResource)) machineAd.setAdUrl(videoResource.getUrl());
        }
    }

    /**
     * 获取设备广告列表
     *
     * @param machineAdVo 机器广告
     * @return {@link List}<{@link MachineAdDto}>
     */
    @Override
    public List<MachineAdDto> queryMachineAdVoByPage(MachineAdVo machineAdVo) {
        resetAdvertisingStatus();

        List<MachineAdDto> machineAdDtos = this.machineAdMapper.queryMachineAdVoByPage(machineAdVo);
        if (CollUtil.isEmpty(machineAdDtos)) return Collections.emptyList();

        return machineAdDtos.stream().peek(machineAdDto -> {

            this.parameterAdFilling(machineAdDto);

            String machineId = machineAdDto.getMachineId();
            List<String> machineIds = Arrays.stream(machineId.split(",")).toList();
            List<String> machineNames = this.machineService.getMachineNamesByMachineIds(machineIds);

            machineAdDto.setMachineIds(machineIds);
            machineAdDto.setMachineNames(machineNames);

        }).toList();


    }

    /**
     * 新增设备广告
     *
     * @param machineAdVo 机器广告
     * @return boolean
     */
    @Override
    public R<String> addMachineAd(MachineAdAddVo machineAdVo) {

        String machineId = machineAdVo.getMachineId();
        if (!StringUtil.isEmpty(machineId)) {
            List<String> list = Arrays.asList(machineId.split(","));
            machineAdVo.setMachineIds(list);
        }
        // 校验是否存在  设备、广告、投放位置都相同，且不是过期状态的数据
        List<MachineAd> list = machineAdMapper.getMachineAd(machineAdVo);
        if (!CollUtil.isEmpty(list)) {
            return R.fail("同位置不可设置重复广告信息，请重试");
        }

        List<Integer> palyAdPositions = new ArrayList<>();
        // 如果参数为 1：主屏全屏 或 3：主屏主页  则查看设备是否存在副屏广告
        if (machineAdVo.getPalyAdPosition() == 1 || machineAdVo.getPalyAdPosition() == 3) {
            palyAdPositions.add(2);
            palyAdPositions.add(4);
        } else {
            palyAdPositions.add(1);
            palyAdPositions.add(3);
        }

        for (String maId : machineAdVo.getMachineIds()) {
            // 几代机
            Integer type = Integer.valueOf(maId.substring(6, 8));
            // 如果是3代机或5代机，则要判断设备是否存在别的屏幕广告
            if ((type == 3 || type == 5) && machineAdVo.getAdType() == 2) {
                List<MachineAd> madList = machineAdMapper.getByMachineIdAndPalyAdPosition(maId,palyAdPositions,machineAdVo.getPalyBeginTime(),machineAdVo.getPlayEndTime());
                if (!CollUtil.isEmpty(madList)) {
                    return R.fail("抱歉，当前视频广告位置因设备限制，暂不支持投放多个视频广告～");
                }
            }
        }

        MachineAd machineAd = new MachineAd();
        BeanUtils.copyBeanProp(machineAd, machineAdVo);

        machineAd.setPalyStatus(PalyStatusEnum.UNSTART.getCode());
        machineAd.setUpdateOper(SecurityUtils.getLoginUser().getSysUser().getNickName());

        this.machineAdMapper.insert(machineAd);
        return R.ok("新增成功");
    }

    /**
     * 通过设备广告id查询设备广告详情
     *
     * @param machineAdId 机器广告id
     * @return {@link MachineAdDto}
     */
    @Override
    public MachineAdDto queryMachineAdInfoById(Long machineAdId) {
        MachineAd machineAd = this.machineAdMapper.queryById(machineAdId);
        Assert.notNull(machineAd, "未查询到设备广告信息");

        MachineAdDto machineAdDto = new MachineAdDto();
        BeanUtils.copyBeanProp(machineAdDto, machineAd);
        AdResource adResource = this.adResourceService.queryById(machineAdDto.getAdId());

        if (ObjectUtil.isNotNull(adResource)) machineAdDto.setResourceId(adResource.getResourceId());

        parameterAdFilling(machineAdDto);

        MachineDto machineDto = this.machineService.queryMachineInfoById(machineAdDto.getMachineId());
        if (ObjectUtil.isNotNull(machineDto)) {

            machineAdDto.setMachineName(machineDto.getMachineName());
            machineAdDto.setMachineSn(machineDto.getMachineSn());

        }

        return machineAdDto;
    }


    /**
     * 删除设备广告
     *
     * @param machineAdId 机器广告id
     * @return boolean
     */
    @Override
    public boolean deleteMachineAd(Long machineAdId) {
        MachineAd machineAd = this.machineAdMapper.queryById(machineAdId);

        if (Objects.equals(machineAd.getPalyStatus(), PalyStatusEnum.PLAYING.getCode())) {
            throw new ServiceException("此广告正在投放中,不可删除.");
        }

        machineAd.setIsDelete(true);
        machineAd.setUpdateOper(SecurityUtils.getLoginUser().getSysUser().getNickName());

        return this.machineAdMapper.update(machineAd) > 0;
    }

    /**
     * 播放/暂停设备广告
     *
     * @param machineAdId 设备广告id
     * @param palyStatus  正在播放
     * @return boolean
     */
    @Override
    public boolean playMachineAd(Long machineAdId, Integer palyStatus) {
        MachineAd machineAd = this.machineAdMapper.queryById(machineAdId);
        Assert.notNull(machineAd, "未查询到设备广告信息");

        PalyStatusEnum playStatusEnum = PalyStatusEnum.getPlayStatusEnum(machineAd.getPalyBeginTime(), machineAd.getPlayEndTime());

        if (playStatusEnum.getCode().equals(PalyStatusEnum.END.getCode())) {
            throw new ServiceException("当前广告已经播放结束");
        }

        AdResource adResource = adResourceService.queryById(machineAd.getAdId());
        Assert.notNull(adResource, "未查询到广告资源");

        if (!pushAdStatus(palyStatus, machineAd)) return false;

        if (String.valueOf(palyStatus).equals(EmqxConstants.MachineAdType.TOPIC_PUSH_AD_PLAY)) {
            machineAd.setPalyStatus(PalyStatusEnum.PLAYING.getCode());
        } else {
            machineAd.setPalyStatus(PalyStatusEnum.UNSTART.getCode());
        }

        this.machineAdMapper.update(machineAd);
        return true;
    }

    @Override
    public boolean adPushMachineAd(Long machineAdId) {
        MachineAd machineAd = this.machineAdMapper.queryById(machineAdId);
        List<String> machineIds = Arrays.stream(machineAd.getMachineId().split(",")).toList();
        machineIds.forEach(id -> this.pushMachineAd(machineAd, id));
        return true;
    }

    @Override
    public boolean pushAd(String machineAdId, String machineId) {
        MachineAd machineAd = this.machineAdMapper.queryById(Long.valueOf(machineAdId));
        return this.pushMachineAd(machineAd, machineId);
    }

    private boolean pushAdStatus(Integer isPlay, MachineAd machineAd) {
        JSONObject pushObj = new JSONObject();
        String recordNo = UUID.randomUUID().toString();
        pushObj.set(EmqxConstants.TOPIC_PUSH_RECORD_NO, recordNo);
        pushObj.set(EmqxConstants.TOPIC_PUSH_MSG_TYPE, EmqxConstants.TOPIC_PUSH_AD_PLAYORPAUSE);
        pushObj.set(EmqxConstants.MachineAdType.TOPIC_PUSH_MSG_PLAY_STATUS, isPlay);
        pushObj.set(EmqxConstants.MachineAdType.TOPIC_PUSH_AD_ID, machineAd.getAdId());
        pushObj.set(EmqxConstants.MachineAdType.TOPIC_PUSH_AD_POSITION, machineAd.getPalyAdPosition());
        pushObj.set(EmqxConstants.TOPIC_PUSH_TIME, DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));

        String adMsg = pushObj.toString();
        for (String machineId : Arrays.asList(machineAd.getMachineId().split(","))) {
            try {
                mqttSendClient.publish(false, machineId, adMsg);
            } catch (MqttException e) {
                log.error("推送设备暂停/播放失败, 设备id:{},广告id:{},消息失败:{}", machineAd.getMachineId(), machineAd.getAdId(), e.getMessage());
                return false;
            }
        }



        return true;
    }

    /**
     * 填充广告参数
     *
     * @param machineAdDto 机器ad dto
     */
    private void parameterAdFilling(MachineAdDto machineAdDto) {
        Integer adType = machineAdDto.getAdType();
        Assert.notNull(adType, "广告类型参数有误");

        String adUrl = getAdUrl(adType, machineAdDto.getResourceId());
        if (CharSequenceUtil.isNotEmpty(adUrl)) machineAdDto.setAdUrl(adUrl);

        machineAdDto.setPalyAdPositionName(Objects.requireNonNull(PalyAdPositionEnum.getEnum(machineAdDto.getPalyAdPosition())).getName());
        if (machineAdDto.getPlayEndTime().before(new Date())) {
            machineAdDto.setPalyStatus(PalyStatusEnum.END.getCode());
        }
    }

    private String getAdUrl(Integer adType, String resourceId) {
        if (Objects.equals(DbConstants.machineCorrelation.AD_TYPE_IMG, adType)) {
            ImgResource imgResource = this.imgResourceService.queryById(resourceId);

            if (ObjectUtil.isNotNull(imgResource)) return imgResource.getUrl();
        }
        else if (Objects.equals(DbConstants.machineCorrelation.AD_TYPE_VIDEO, adType)) {

            VideoResource videoResource = this.videoResourceService.queryById(resourceId);
            if (ObjectUtil.isNotNull(videoResource)) return videoResource.getUrl();

        }
        else {
            throw new ServiceException("广告类型不支持");
        }
        return null;
    }


    private void resetAdvertisingStatus() {
        List<MachineAd> machineAds = this.machineAdMapper.queryMachineAdByStatus();
        for (MachineAd machineAd : machineAds) {
            if (machineAd.getPlayEndTime().before(new Date())) {
                machineAd.setPalyStatus(PalyStatusEnum.END.getCode());
                this.machineAdMapper.update(machineAd);
            }
        }
    }
}
